﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Xeml.Document.Contracts;
using Xeml.Document;
using System.Windows.Forms;
using System.Data;
using System.Drawing;
using Xeml.Manager;
using Mpi.Common.Collections;
using XemlUtilities.Indexing;
using System.Diagnostics;
using Mpi.Common.Controls;

namespace XemlInteractiveDesigner
{
    internal class FileManager : IBusyReporter
    {
        private IDocument currentXemlDoc;
        private IDocument dummyXemlDoc;
        private string filePath;
        private bool unsavedChanges;
        private bool hasErrors;
        //private ValidationLog.LogDataTable validationLog;
        private bool invalidDocument;


        private List<IValidator> validators;
       // private IdentifiableObjectDictionary<string, IValidator> paramResources;
        private Timer _timer;
        private Stack<string> _undoStack;
        private Stack<string> _redoStack;


        //public IdentifiableObjectDictionary<string, IValidator> ParamResources
        //{
        //    get
        //    {
        //        return this.paramResources;
        //    }
        //}


        //private EditMode editMode;
        //private string sourceCodeCache;


        //public string SourceCodeCache
        //{
        //    get
        //    {
        //        return sourceCodeCache;
        //    }

        //}

        //public ValidationLog.LogDataTable ValidationLog
        //{
        //    get
        //    {
        //        return validationLog;
        //    }

        //}


        public bool UnsavedChanges
        {
            get
            {
                return this.unsavedChanges;
            }
        }

        //public EditMode EditMode
        //{
        //    get { return editMode; }
        //}

        public bool InvalidDocument
        {
            get { return invalidDocument; }
        }


        public bool HasErrors
        {
            get { return hasErrors; }
        }

        public string FilePath
        {
            get { return filePath; }
        }

        public IDocument CurrentXemlDoc
        {
            get
            {
                return this.currentXemlDoc;
            }
        }

        //public int LogErrorCount
        //{
        //    get
        //    {
        //        return validationLog.Select("SeverityCode='Error'").Length;
        //    }
        //}

        //public int LogWarningCount
        //{
        //    get
        //    {
        //        return validationLog.Select("SeverityCode='Warning'").Length;
        //    }
        //}

        //public int LogInfoCount
        //{
        //    get
        //    {
        //        return validationLog.Select("SeverityCode='Info'").Length;
        //    }
        //}


        public FileManager()
        {
            InitDirectories();
            ValidatorManager.Instance.ToString();
            InitValidator();
            this.dummyXemlDoc = new Xeml.Document.XemlDocument();

            //this.validationLog = new ValidationLog.LogDataTable();
            //this.validationLog.LogRowChanged += new ValidationLog.LogRowChangeEventHandler(validationLog_LogRowChanged);
            //this.validationLog.TableCleared += new DataTableClearEventHandler(validationLog_TableCleared);


            _timer = new Timer();
            _timer.Interval = 500;
            _timer.Tick += new EventHandler(_timer_Tick);
            _undoStack = new Stack<string>(5);
            _redoStack = new Stack<string>(5);


            //validationLog.Columns.Add("SeverityIcon", typeof(Icon));
            //validationLog.Columns.Add("Severity", typeof(string));
            //validationLog.Columns.Add("Message", typeof(string));
            //validationLog.Columns.Add("RelatedObject", typeof(string));

        }

        void _timer_Tick(object sender, EventArgs e)
        {
            Debug.WriteLine("FileMgr: Doc changed tick");
            _timer.Stop();

            currentXemlDoc.RefreshXeml();
            _undoStack.Push(currentXemlDoc.Xeml);

            if (!unsavedChanges && SomethingChanged != null)
            {
                this.unsavedChanges = true;
                SomethingChanged(this, new EventArgs());
            }

            if (UndoAvailabilityChanged != null)
                UndoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(true));

        }

        public void Undo()
        {
            if (_undoStack.Count > 0)
            {
                OnBackgroundWorkStarted("Performing undo action.");
                try
                {
                    _redoStack.Push(_undoStack.Pop());

                    this.SetXemlCode(_undoStack.Peek(), false, false);

                    if (_undoStack.Count < 2)
                    {
                        if (UndoAvailabilityChanged != null)
                            UndoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(false));
                    }


                    if (RedoAvailabilityChanged != null)
                        RedoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(true));
                }
                finally
                {
                    OnBackgroundWorkEnded();
                }
            }
        }

        public void Redo()
        {
            if (_redoStack.Count > 0)
            {
                OnBackgroundWorkStarted("Performing redo action.");
                try
                {
                    _undoStack.Push(_redoStack.Pop());
                    this.SetXemlCode(_undoStack.Peek(), false, false);

                    if (UndoAvailabilityChanged != null)
                        UndoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(true));

                    if (_redoStack.Count < 1)
                    {
                        if (RedoAvailabilityChanged != null)
                            RedoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(false));
                    }
                }
                finally
                {
                    OnBackgroundWorkEnded();
                }
            }
        }

        /// <summary>
        /// Initializes neccessary directories and files
        /// </summary>
        public void InitDirectories()
        {
            //if (string.IsNullOrEmpty(XemlCompiler.Properties.Settings.Default.StoreFolder))
            //{
            //    string pstore = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
            //    XemlCompiler.Properties.Settings.Default.StoreFolder = Path.Combine(pstore, "XEMLStore");
            //    XemlCompiler.Properties.Settings.Default.Save();
            //}
            //if (string.IsNullOrEmpty(Properties.Settings.Default.TemplateFolder))
            //{
            //    string ptempl = Path.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
            //    XemlCompiler.Properties.Settings.Default.TemplateFolder = Path.Combine(ptempl, "XEMLStore" + Path.DirectorySeparatorChar + "Templates");
            //    XemlCompiler.Properties.Settings.Default.Save();
            //}
            //if (!Directory.Exists(@Properties.Settings.Default.StoreFolder))
            //{
            //    Directory.CreateDirectory(@Properties.Settings.Default.StoreFolder);
            //}
            //if (!Directory.Exists(@Properties.Settings.Default.TemplateFolder))
            //{
            //    Directory.CreateDirectory(@Properties.Settings.Default.TemplateFolder);
            //}

            //File.WriteAllBytes(Path.Combine(Properties.Settings.Default.TemplateFolder, "Standard.xeml"), MainResources.Standard);
        }

        void validationLog_TableCleared(object sender, DataTableClearEventArgs e)
        {
            if (LogChanged != null)
                LogChanged(this, new EventArgs());
        }

        //void validationLog_LogRowChanged(object sender, ValidationLog.LogRowChangeEvent e)
        //{
        //    if (LogChanged != null)
        //        LogChanged(this, new EventArgs());
        //}


        /// <summary>
        /// Loads a document from a given path.
        /// </summary>
        /// <param name="path">The path to the document.</param>
        /// <param name="asTemplate">Indicates wether the document should be handled as template or not.</param>
        public void LoadDocument(string @path, bool asTemplate)
        {
            OnBackgroundWorkStarted("Loading \"" + path + "\"");
            try
            {
                if (this.currentXemlDoc != null)
                {
                //    this.currentXemlDoc.Validation -= new EventHandler<ValidationMessage>(OnXemlDocValidation);
                    this.currentXemlDoc.Changed -= new EventHandler(OnCurrentXemlDocChanged);
                }

                if (File.Exists(path))
                {
                    this.unsavedChanges = false;
                    this.currentXemlDoc = new Xeml.Document.XemlDocument();
                 //   this.currentXemlDoc.Validation += new EventHandler<ValidationMessage>(OnXemlDocValidation);
                   

                    SetXemlCode(LoadXemlCode(path), false, false);
                    //SetDesignMode();

                    if (asTemplate)
                    {
                        this.currentXemlDoc.NewId();
                        //this.currentXemlDoc.Name = "New Experiment";
                        this.filePath = "";
                        if (this.FilePathChanged != null)
                            this.FilePathChanged(this, new EventArgs());
                    }
                    SampleIndexer.Instance.Init(this.currentXemlDoc);
                    OnFileLoaded();
                    this.currentXemlDoc.Changed += new EventHandler(OnCurrentXemlDocChanged);
                }
                else
                {
                    MessageBox.Show("File " + path + " not found", "File not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    ///InitNewDocument();
                }
            }

            finally
            {

                OnBackgroundWorkEnded();
            }
        }




        /// <summary>
        /// Creates a new xeml document.
        /// </summary>
        //public void New()
        //{

        //    OnBackgroundWorkStarted("Creating new document.");
        //    try
        //    {

        //        if (currentXemlDoc != null)
        //        {
        //            this.currentXemlDoc.Validation -= new EventHandler<ValidationMessage>(OnXemlDocValidation);
        //            this.currentXemlDoc.Changed -= new EventHandler(OnCurrentXemlDocChanged);
        //        }

        //      //  string path = Path.Combine(Properties.Settings.Default.TemplateFolder, "Standard.xeml");
        //        if (File.Exists(path))
        //        {
        //            LoadDocument(path, true);
        //        }
        //        else
        //        {
        //            this.hasErrors = false;
        //            this.invalidDocument = false;
        //            this.currentXemlDoc = new Xeml.Document.XemlDocument();
        //            this.currentXemlDoc.Validation += new EventHandler<ValidationMessage>(OnXemlDocValidation);
        //            this.currentXemlDoc.Changed += new EventHandler(OnCurrentXemlDocChanged);
        //            this.currentXemlDoc.RefreshXeml();
        //            SetXemlCode(this.currentXemlDoc.Xeml, false, true);
        //            //SetDesignMode();
        //            //this.currentXemlDoc.SchemaLocation = Application.StartupPath + "\\XEMLSchema_v2.xsd";
        //            this.filePath = "";
        //            this.unsavedChanges = false;
        //            OnFileLoaded();
        //            if (FileSaved != null)
        //                FileSaved(this, new EventArgs());
        //            //this.currentXemlDoc.Changed += new EventHandler(OnCurrentXemlDocChanged);

        //            SampleIndexer.Instance.Init(this.currentXemlDoc);
        //        }
        //    }
        //    finally
        //    {

        //        OnBackgroundWorkEnded();
        //    }
        //}


        /// <summary>
        /// Saves the current xeml document,
        /// </summary>
        /// <param name="path">The path to the target file.</param>
        public void SaveDocument(string @path)
        {
            string savePath = path;

            try
            {
                if (String.IsNullOrEmpty(savePath) && FilePathNeeded != null)
                {
                    FilePathNeededEventArgs fpn = new FilePathNeededEventArgs();
                    FilePathNeeded(this, fpn);
                    if (fpn.Cancel)
                        return;

                    savePath = fpn.Path;
                }

                this.currentXemlDoc.Save(savePath);

                if (this.filePath != savePath)
                {
                    this.filePath = savePath;
                    if (FilePathChanged != null)
                        FilePathChanged(this, new EventArgs());
                }

                OnFileSaved();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void OnFileSaved()
        {
            this.unsavedChanges = false;
            if (FileSaved != null)
                FileSaved(this, new EventArgs());
        }


        /// <summary>
        /// Saves the current xeml document to the current path.
        /// </summary>
        public void SaveDocument()
        {
            SaveDocument(this.filePath);
        }

        /// <summary>
        /// Saves the current xeml document to the current path.
        /// </summary>
        public void SaveDocumentAs()
        {
            if (FilePathNeeded != null)
            {
                FilePathNeededEventArgs fpn = new FilePathNeededEventArgs();
                fpn.Path = this.filePath;
                FilePathNeeded(this, fpn);
                if (fpn.Cancel)
                    return;
                else
                    SaveDocument(fpn.Path);
            }

        }

        /// <summary>
        /// Loads a xeml document.
        /// </summary>
        /// <param name="path">The path to the xeml file.</param>
        /// <returns></returns>
        private string LoadXemlCode(string path)
        {
            this.filePath = path;
            if (FilePathChanged != null)
                FilePathChanged(this, new EventArgs());
            return File.ReadAllText(path);
        }


        /// <summary>
        /// Set's xeml source code to the current document.
        /// </summary>
        /// <param name="xeml">The xeml source code.</param>
        /// <remarks>The usage alters the manager into the source code mode</remarks>
        public void SetXemlCode(string xeml, bool raiseSomethingChangedEvent, bool doValidation)
        {


            this.currentXemlDoc.Load(xeml);

            //if (doValidation)
            //    DoValidation(false);


            if (!unsavedChanges && raiseSomethingChangedEvent && SomethingChanged != null)
            {
                this.unsavedChanges = true;
                SomethingChanged(this, new EventArgs());
            }
        }



        private void InitValidator()
        {

            this.validators = new List<IValidator>();
            foreach (string ure in ValidatorManager.Instance.ValidationComponents.Keys)
            {
                IValidator val = ValidatorManager.Instance.CreateValidator(ure);
                this.validators.Add(val);
          //      val.Validation += new EventHandler<ValidationMessage>(OnXemlDocValidation);
            }
        }

        //public void DoValidation(bool withOntolgies)
        //{

        ////    ResetValidation(false);
        //    this.currentXemlDoc.DoXmlValidation();

        //    foreach (IValidator iv in this.validators)
        //    {
        //        iv.Validate(this.currentXemlDoc);
        //    }

        //    if (withOntolgies)
        //    {
        //        foreach (OntologyHandlerResource pvr in currentXemlDoc.DocumentResources.EnvironmentOntologyHandler)
        //        {
        //            if (pvr.ComponentState == ComponentLoadState.Loaded)
        //            {
        //                pvr.Handler.Validation += new EventHandler<ValidationMessage>(OnXemlDocValidation);
        //                pvr.Handler.Validate(this.currentXemlDoc);
        //                pvr.Handler.Validation -= new EventHandler<ValidationMessage>(OnXemlDocValidation);
        //            }
        //        }
        //    }
        //}

        //private void ResetValidation(bool holdXemlDocMsgs)
        //{
        //    this.hasErrors = false;
        //    this.invalidDocument = false;

        //    List<DataRow> toRem = new List<DataRow>();

        //    if (holdXemlDocMsgs)
        //    {
        //        foreach (DataRow row in validationLog.Select("Source <> 'XemlDocument'"))
        //        {
        //            validationLog.Rows.Remove(row);
        //        }
        //    }
        //    else
        //        validationLog.Rows.Clear();
        //}

        #region Event- /handling

        public event EventHandler FilePathChanged;
        public event EventHandler FileLoaded;
        public event EventHandler FileSaved;
        public event EventHandler SomethingChanged;
        public event EventHandler EditModeChanged;
        public event EventHandler DesignModeRejected;
        public event EventHandler<FilePathNeededEventArgs> FilePathNeeded;
        public event EventHandler LogChanged;
        public event EventHandler ValidationStarted;
        public event EventHandler<UndoRedoAvailabilityEventArgs> UndoAvailabilityChanged;
        public event EventHandler<UndoRedoAvailabilityEventArgs> RedoAvailabilityChanged;

        public event EventHandler<BusyEventArgs> BackgroundWorkStarted;
        public event EventHandler BackgroundWorkEnded;

        protected virtual void OnBackgroundWorkStarted(string message)
        {
            if (BackgroundWorkStarted != null)
                BackgroundWorkStarted(this, new BusyEventArgs(message));
        }

        protected virtual void OnBackgroundWorkEnded()
        {
            if (BackgroundWorkEnded != null)
                BackgroundWorkEnded(this, new EventArgs());
        }



        protected virtual void OnFileLoaded()
        {
            _undoStack.Clear();
            _redoStack.Clear();

            _undoStack.Push(currentXemlDoc.Xeml);
            if (FileLoaded != null)
                FileLoaded(this, new EventArgs());

            if (UndoAvailabilityChanged != null)
                UndoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(false));
            if (RedoAvailabilityChanged != null)
                RedoAvailabilityChanged(this, new UndoRedoAvailabilityEventArgs(false));
        }

        //void OnXemlDocValidation(object sender, ValidationMessage e)
        //{
        //    IValidator iv = sender as IValidator;

        //    ValidationLog.LogRow lr = validationLog.NewLogRow();
        //    switch (e.SeverityCode)
        //    {
        //        case SeverityCode.Error:
        //            {
        //                this.hasErrors = true;
        //                lr.SeverityCode = "Error";
        //                break;
        //            }
        //        case SeverityCode.Message:
        //            {
        //                lr.SeverityCode = "Info";
        //                break;
        //            }
        //        case SeverityCode.Warning:
        //            {
        //                lr.SeverityCode = "Warning";
        //                break;
        //            }
        //    }

        //    lr.Message = e.Text;
        //    if (e.RelatedObject != null)
        //        lr.RelatedObject = e.RelatedObject.ToString();
        //    //string[] className = sender.ToString().Split('.');
        //    //  if (e.RelatedObject != null)


        //    lr.Source = sender.GetType().Name;// className[className.Length - 1];



        //    if (e.InhibitRendering)
        //    {
        //        this.invalidDocument = true;
        //        lr.DocumentError = true;
        //    }
        //    else
        //    {
        //        lr.DocumentError = false;
        //    }

        //    validationLog.Rows.Add(lr);
        //}

        void OnCurrentXemlDocChanged(object sender, EventArgs e)
        {
            Debug.WriteLine("FileMgr: Doc changed");
            _timer.Stop();


            //ResetValidation(true);
            //DoValidation();


            _timer.Start();
        }
        #endregion


    }

    /// <summary>
    /// A edit mode enumeration.
    /// </summary>
    internal enum EditMode
    {
        DesignMode,
        CodeMode
    }

    /// <summary>
    /// Event args for requesting a file path.
    /// </summary>
    public class FilePathNeededEventArgs : EventArgs
    {
        private string path;
        private bool cancel = false;

        public bool Cancel
        {
            get { return cancel; }
            set { cancel = value; }
        }


        public string Path
        {
            get { return path; }
            set { path = value; }
        }

    }

    public class UndoRedoAvailabilityEventArgs : EventArgs
    {
        private bool _available;
        public bool Available
        {
            get { return _available; }
        }

        public UndoRedoAvailabilityEventArgs(bool avail)
        {
            _available = avail;
        }
    }
}

